/**
 * @file extra_i2s.xc
 * @author Dylan (2481757782@qq.com)
 * @brief 
 * @version 0.1
 * @date 2025-7-10
 * 
 * @copyright Copyright (c) 2025
 * Copyright 2021-2024 XMOS LIMITED.
 * This Software is subject to the terms of the XMOS Public Licence: Version 1.
 */

#include <platform.h>
#include <xs1.h>
#include "i2s.h"
#include <print.h>
#include <stdlib.h>
#include <user_main.h>
#include <xua_conf.h>

#include "eq_biquad.h"


#ifndef NUM_I2S2_CHAN_ADC
#define NUM_I2S2_CHAN_ADC  (I2S_CHANS_ADC)
#endif

#ifndef NUM_I2S2_CHAN_DAC
#define NUM_I2S2_CHAN_DAC (0)
#endif


#define DATA_BITS (32)

enum eq_conf
{
    EQ_CONF_EN  =  1,
    EQ_CONF_GAIN ,
};

unsafe chanend uc_i2s;

void UserBufferManagementInit(unsigned sampFreq)
{
    
}

unsigned counter = 0;

#pragma unsafe arrays
void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudioToUsb[])
{
    unsafe
    {
        outuint((chanend)uc_i2s, 1);
        for(size_t i = 0; i < I2S2_CHANS_DAC; i++)
        {
            outuint((chanend)uc_i2s, sampsFromUsbToAudio[i + NUM_I2S2_CHAN_DAC]);
        }
        outct((chanend)uc_i2s, XS1_CT_END);
        for(size_t i = 0; i< I2S2_CHANS_ADC; i++)
        {
            sampsFromAudioToUsb[i + NUM_I2S2_CHAN_ADC] = inuint((chanend) uc_i2s);
        }
        chkct((chanend)uc_i2s, XS1_CT_END);
    }
}



void i2s_data(server i2s_frame_callback_if i_i2s, chanend c)
{
    unsigned x;
    unsigned samplesIn[I2S2_CHANS_ADC];
    unsigned samplesOut[I2S2_CHANS_DAC];

    while (1)
    {
        select
        {
            case inuint_byref(c, x):
#pragma loop unroll
                for(size_t i = 0; i< I2S2_CHANS_DAC; i++)
                {
                    samplesOut[i] = inuint(c);
                }
                chkct(c, XS1_CT_END);

#pragma loop unroll
                for(size_t i = 0; i< I2S2_CHANS_ADC; i++)
                {
                    outuint(c, samplesIn[i]);
                }
                outct(c, XS1_CT_END);

                break;

            case i_i2s.init(i2s_config_t &?i2s_config, tdm_config_t &?tdm_config):
                i2s_config.mode = I2S_MODE_I2S;
                //  i2s_config.mclk_bclk_ratio = (MASTER_CLOCK_FREQUENCY / (SAMPLE_FREQUENCY*2*DATA_BITS));
                //  i2s_config.mode = I2S_MODE_LEFT_JUSTIFIED;
                break;

            case i_i2s.restart_check() -> i2s_restart_t restart:
                // Inform the I2S slave whether it should restart or exit
                restart = I2S_NO_RESTART;
                break;

            case i_i2s.receive(size_t num_in, int32_t samples[num_in]):
#pragma loop unroll
                for(size_t i = 0; i < I2S2_CHANS_ADC; i++)
                {
                    samplesIn[i] = samples[i];
                }
                break;

            case i_i2s.send(size_t num_out, int32_t samples[num_out]):
#pragma loop unroll
                for(size_t i = 0; i < I2S2_CHANS_DAC; i++)
                {
                    samples[i] = samplesOut[i];
                }
                break;
        }
    }
}



void eq_audio_filter( chanend c, chanend c_out, chanend c_eq) 
{
        unsigned  tmp;
        unsigned samplesIn[I2S2_CHANS_ADC];
        unsigned samplesOut[I2S2_CHANS_DAC];
        unsigned eq_enabled=1;
        unsigned eq_val = 0;

        eq_setBiquad(0, 0, bq_type_peak, 500.0 / 48000.0, 0.7071f, 0);
        eq_setBiquad(0, 1, bq_type_peak, 1000.0 / 48000.0, 0.7071f, 0);
        eq_setBiquad(0, 2, bq_type_peak, 1500.0 / 48000.0, 0.7071f, 0);
        while (1) 
        {
            /* Exchanges samples with the i2s handler */
            select
            {
                case inuint_byref(c, tmp):
                {
                    outuint(c_out, 1);
                    for(size_t i = 0; i< I2S2_CHANS_DAC; i++)
                    {
                        samplesOut[i] = inuint(c);
                        outuint(c_out, samplesOut[i]);
                    }
                    chkct(c, XS1_CT_END);
                    outct(c_out, XS1_CT_END);
                    for(size_t i = 0; i< I2S2_CHANS_ADC; i++)
                    {
                        outuint(c, samplesIn[i]);
                        samplesIn[i] = inuint(c_out);
                    }
                    outct(c, XS1_CT_END);
                    chkct(c_out, XS1_CT_END);


                    // if(eq_enabled)
                    // {
                          //samplesIn[0] = lowpass_48khz_fc800hz(samplesIn[0]);
                          samplesIn[0] = eq_process(0, samplesIn[0]);
                   // }
                }
                break;

                case inuint_byref(c_eq, tmp):
                {
                        if (EQ_CONF_EN == tmp)
                        {
                            chkct(c_eq, XS1_CT_END);
                            eq_enabled = inuint(c_eq);
                            outct(c_eq, XS1_CT_END);
                            if( eq_enabled )
                                eq_setBiquad(0, 1, bq_type_peak, 1000.0 / 48000.0, 20.0f, 12);
                            else
                                eq_setBiquad(0, 1, bq_type_peak, 1000.0 / 48000.0, 20.0f, -12);
                        }
                        else if(EQ_CONF_GAIN == tmp)
                        {
                            chkct(c_eq, XS1_CT_END);
                            eq_val = inuint(c_eq);
                            outct(c_eq, XS1_CT_END);
                            double eq_gain = (int)eq_val;
                            eq_setBiquad(0, 1, bq_type_peak, 1000.0 / 48000.0, 0.7071f, eq_gain);
                        }
                }
                break;
            }
        }
}

extern unsigned Set_uart_count, Set_samplingRate, Set_eq_spectrum, Set_gain;
on tile[0]: in port p_key   = XS1_PORT_4E;
on tile[0]: out port p_led   = XS1_PORT_4F;

void key_driver(chanend c_eq) 
{
    unsigned stable_value;
    timer t;
    unsigned debounce_time = 20000;  // 20ms 去抖时间

    unsigned eq_enabled = 1; // EQ启用状态
    int get_gain = 0;
    char led_state = 0b0000; // LED状态
   
    printf("size:%d\n",sizeof(eq_packet_t));
    while (1) 
    {
        select {
            case p_key when pinsneq(stable_value) :> unsigned new_value:
            {
                unsigned start_time;
                t :>  start_time;
                // 等待稳定
                t when timerafter(start_time + debounce_time) :> void;
                p_key :> new_value;  // 重新读取
                
                if (new_value != stable_value) {
                    stable_value = new_value;
                    
                    //printf("key :%x\n", stable_value);
                    if(!(stable_value & 0x01)) // 按下第一个按键
                    {
                        // if(eq_enabled)
                        //    led_state |= 0b0001; // 保持LED状态
                        // else
                        //     led_state &= 0b1110; // 保持LED状态

                        // 切换EQ启用状态
                        outuint(c_eq, EQ_CONF_EN);
                        outct(c_eq, XS1_CT_END);
                        outuint(c_eq, eq_enabled);
                        chkct(c_eq, XS1_CT_END);

                        eq_enabled = !eq_enabled; // 切换状态
                    }
                    else if(!(stable_value & 0x02)) // 按下第二个按键
                    {
                        // if(get_gain > -12)
                        // {
                        //     get_gain--;
                        //     led_state &= 0b1001; // 保持LED状态
                        // }
                        // else{
                        //     get_gain = -12;
                        //     led_state |= 0b0010; // 保持LED状态
                        //     led_state &= 0b1011; // 保持LED状态
                        // }

                        // if(get_gain == 0)
                        // {
                        //     led_state &= 0b0000; // 保持LED状态
                        // }
                            
                        // 获取当前EQ配置
                        outuint(c_eq, EQ_CONF_GAIN);
                        outct(c_eq, XS1_CT_END);
                        outuint(c_eq, get_gain);
                        chkct(c_eq, XS1_CT_END);
                    }
                    else if(!(stable_value & 0x04)) // 按下第三个按键
                    {
                        // if(get_gain < 12)
                        // {
                        //     get_gain++;
                        //     led_state &= 0b1001; // 保持LED状态
                        // }
                        // else{
                        //     get_gain = 12;  
                        //     led_state |= 0b0100; // 保持LED状态
                        //     led_state &= 0b1101; // 保持LED状态
                        // }

                        // if(get_gain == 0)
                        // {
                        //     led_state &= 0b0000; // 保持LED状态
                        // }
                           
                        outuint(c_eq, EQ_CONF_GAIN);
                        outct(c_eq, XS1_CT_END);
                        outuint(c_eq, get_gain);
                        chkct(c_eq, XS1_CT_END);
                    }

                    p_led <: led_state;
                }
            }
            break;
        }
    }
}

on tile[1]: out buffered port:32 p_i2s_dout[1] = {XS1_PORT_1M}; // X1D36
on tile[1]: in buffered port:32 p_i2s_din[1] = {XS1_PORT_1L}; // X1D36
on tile[1]: in port p_i2s_bclk = XS1_PORT_1A;                 // X1D38
on tile[1]: in port p_i2s_mclk = XS1_PORT_1H;                 // X1D38
on tile[1]: in buffered port:32 p_i2s_lrclk = XS1_PORT_1G;    // X1D39

on tile[1]: clock clk_bclk = XS1_CLKBLK_1;

void i2s_driver(chanend c, chanend c_eq)
{
    interface i2s_frame_callback_if i_i2s;
    chan effect_c;
    par
    {
        i2s_frame_slave(i_i2s, p_i2s_dout, 1, p_i2s_din, 1, DATA_BITS, p_i2s_bclk, p_i2s_lrclk, clk_bclk);
        eq_audio_filter(c, effect_c, c_eq);
        i2s_data(i_i2s, effect_c);
    }

    return;
}
